﻿using System;
using System.Collections.Specialized;
using System.Linq;
using BlueCrest.Prism.Common.UI.Ribbon;
using BlueCrest.Prism.Common.UI.Ribbon.Entities;
using Infragistics.Windows.Ribbon;
using Microsoft.Practices.Prism.Regions;

namespace BlueCrest.Prism.Common.UI.Prism.Adapters
{
    public class XamRibbonRegionAdapter : RegionAdapterBase<XamRibbon>
    {
        public XamRibbonRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
        {

        }

        /// <summary>
        /// Template method to adapt the object to an <see cref="T:Microsoft.Practices.Prism.Regions.IRegion"/>.
        /// </summary>
        /// <param name="region">The new region being used.</param><param name="regionTarget">The object to adapt.</param>
        protected override void Adapt(IRegion region, XamRibbon regionTarget)
        {
            if(region == null) throw new ArgumentNullException("region");
            if (regionTarget == null) throw new ArgumentNullException("regionTarget");

            region.ActiveViews.CollectionChanged += (s, args) =>
                    {
                        switch (args.Action)
                        {
                            case NotifyCollectionChangedAction.Add:

                                foreach (Object view in args.NewItems)
                                {
                                    AddViewToRegion(view, regionTarget);
                                }
                                break;

                            case NotifyCollectionChangedAction.Remove:
                                foreach (Object view in args.OldItems)
                                {
                                    RemoveViewFromRegion(view, regionTarget);
                                }
                                break;
                            
                            
                        }
                    };
        }

        private void RemoveViewFromRegion(object view, XamRibbon regionTarget)
        {
            //TODO: removal logic needs to be implemented if you want to have run-time ribbon items created on the fly (when module loads/unloads
        }

        private void AddViewToRegion(object view, XamRibbon regionTarget)
        {
            var ribbonEstate = view as RibbonEstate;

            if (ribbonEstate != null)
            {
                #region Handle Tabs

                if(ribbonEstate.HasTabs)
                {
                    var list = ribbonEstate.Tabs.ToList();

                    foreach (var ribbonTab in list)
                    {
                        RibbonTabItem ribbonTabItem;

                        bool tabAlreadyExists = TryFindOrCreateRibbonTab(regionTarget, ribbonTab.TabName, out ribbonTabItem);

                        //loop through the groups
                        foreach (var ribbonGroup in ribbonTab.RibbonGroups)
                        {
                            RibbonGroup group;

                            var groupFound = TryFindOrCreateRibbonGroup(ribbonTabItem, ribbonGroup.GroupName, out group);

                            //add each item to the group
                            foreach (var ribbonItem in ribbonGroup.RibbonItems)
                            {
                                switch (ribbonItem.ItemType)
                                {
                                    case RibbonItemType.Button:
                                        var item = new ButtonTool
                                        {
                                            Content = ribbonItem.Name,
                                            Command = ribbonItem.Command,
                                        };

                                        group.Items.Add(item);
                                        break;
                                    default:
                                        throw new ArgumentOutOfRangeException();
                                }
                            }

                            if (!groupFound)
                            {
                                //only add the new groups
                                ribbonTabItem.RibbonGroups.Add(group);
                            }

                        }

                        if (!tabAlreadyExists)
                        {
                            //we only add the new tabs (any existing ones will be ignored
                            regionTarget.Tabs.Add(ribbonTabItem);
                        }

                    } 
                }
                
                #endregion

                #region Handle Application Menu
                
                if(ribbonEstate.HasApplicationMenu)
                {
                    var appMenus = ribbonEstate.ApplicationMenu;

                    foreach (var ribbonItem in appMenus)
                    {
                        switch (ribbonItem.ItemType)
                        {
                            case RibbonItemType.Button:
                                var item = new ButtonTool
                                {
                                    Content = ribbonItem.Name,
                                    Command = ribbonItem.Command,
                                };

                                regionTarget.ApplicationMenu.Items.Add(item);
                                break;

                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }
                }


                #endregion
            }
        }

        /// <summary>
        /// Tries to find the existing RibbonGroup with the given name, if it finds then returns the existing, otherwise creates a new one
        /// </summary>
        /// <param name="ribbonTabItem"></param>
        /// <param name="groupName"></param>
        /// <param name="ribbonGroup"></param>
        /// <returns>True if existing RibbonGroup found, otherwise False</returns>
        private bool TryFindOrCreateRibbonGroup(RibbonTabItem ribbonTabItem, string groupName, out RibbonGroup ribbonGroup)
        {
            RibbonGroup foundRibbonGroup = ribbonTabItem.RibbonGroups.FirstOrDefault(a => a.Caption.ToString() == groupName);

            if (foundRibbonGroup == null)
            {
                ribbonGroup = new RibbonGroup
                {
                    Caption = groupName,
                };
                return false;
            }

            ribbonGroup = foundRibbonGroup;
            return true;
        }

        /// <summary>
        /// Check if Ribbon Tab Item already exists, if not create it, otherwise returns found one
        /// </summary>
        /// <param name="ribbon"></param>
        /// <param name="tabName"></param>
        /// <param name="ribbonTabItem"></param>
        /// <returns>True if existing RibbonTab found, otherwise False</returns>
        private bool TryFindOrCreateRibbonTab(XamRibbon ribbon, string tabName, out RibbonTabItem ribbonTabItem)
        {
            RibbonTabItem foundRibbonTabItem = ribbon.Tabs.FirstOrDefault(a => a.Header.ToString() == tabName);

            if (foundRibbonTabItem == null)
            {
                ribbonTabItem = new RibbonTabItem
                {
                    Header = tabName,
                };

                return false;
            }

            ribbonTabItem = foundRibbonTabItem;
            return true;
        }

        /// <summary>
        /// Template method to create a new instance of <see cref="T:Microsoft.Practices.Prism.Regions.IRegion"/>
        ///             that will be used to adapt the object.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="T:Microsoft.Practices.Prism.Regions.IRegion"/>.
        /// </returns>
        protected override IRegion CreateRegion()
        {
            return new AllActiveRegion(); 
        }
    }
}
